package com.wolfeyes.framework.service;

import com.wolfeyes.common.constant.FilmSystemConstants;
import com.wolfeyes.framework.springsecurity.domain.model.CustomizeUserDetails;
import com.wolfeyes.common.redis.RedisCache;
import com.wolfeyes.common.utils.network.http.ServletUtils;
import com.wolfeyes.common.utils.character.StringUtils;
import com.wolfeyes.common.utils.network.ip.AddressUtils;
import com.wolfeyes.common.utils.network.ip.IpUtils;
import com.wolfeyes.common.utils.uuid.IdUtils;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description Token令牌-服务层
 * @author yanyljava
 * @create 2020-09-04 17:20
 */
@Component
public class TokenService {

	// 令牌自定义标识
	@Value("${token.header}")
	private String header;

	// 令牌秘钥
	@Value("${token.secret}")
	private String secret;

	// 令牌有效期（默认30分钟）
	@Value("${token.expireTime}")
	private int expireTime;

	protected static final long MILLIS_SECOND = 1000;

	protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

	private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

	@Autowired
	private RedisCache redisCache;

	public CustomizeUserDetails getCustomizeUserDetails(HttpServletRequest request) {
		// 获取请求携带的令牌
		String token = getToken(request);
		if (StringUtils.isNotEmpty(token))
		{
			Claims claims = parseToken(token);
			// 解析对应的权限以及用户信息
			String uuid = (String) claims.get(FilmSystemConstants.LOGIN_USER_KEY);
			String userKey = getTokenKey(uuid);
			CustomizeUserDetails customizeUserDetails = redisCache.getCacheObject(userKey);
			return customizeUserDetails;
		}
		return null;
	}

	/**
	 * @title 设置用户身份信息
	 */
	public void setCustomizeUserDetails(CustomizeUserDetails customizeUserDetails)
	{
		if (StringUtils.isNotNull(customizeUserDetails) && StringUtils.isNotEmpty(customizeUserDetails.getToken())) {
			refreshToken(customizeUserDetails);
		}
	}

	/**
	 * @title 删除用户身份信息
	 */
	public void delCustomizeUserDetails(String token)
	{
		if (StringUtils.isNotEmpty(token))
		{
			String userKey = getTokenKey(token);
			redisCache.deleteObject(userKey);
		}
	}

	/**
	 * @title 创建令牌
	 * @param customizeUserDetails 用户信息
	 * @return 令牌
	 */
	public String createToken(CustomizeUserDetails customizeUserDetails)
	{
		String token = IdUtils.fastUUID();
		customizeUserDetails.setToken(token);
		setUserAgent(customizeUserDetails);
		refreshToken(customizeUserDetails);

		Map<String, Object> claims = new HashMap<>();
		claims.put(FilmSystemConstants.LOGIN_USER_KEY, token);
		return createToken(claims);
	}

	/**
	 * @title 验证令牌有效期，相差不足20分钟，自动刷新缓存
	 * @param customizeUserDetails
	 * @return 令牌
	 */
	public void verifyToken(CustomizeUserDetails customizeUserDetails)
	{
		long expireTime = customizeUserDetails.getExpireTime();
		long currentTime = System.currentTimeMillis();
		if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
		{
			refreshToken(customizeUserDetails);
		}
	}

	/**
	 * @title 刷新令牌有效期
	 * @param customizeUserDetails 登录信息
	 */
	public void refreshToken(CustomizeUserDetails customizeUserDetails) {
		
		customizeUserDetails.setLoginTime(System.currentTimeMillis());
		customizeUserDetails.setExpireTime(customizeUserDetails.getLoginTime() + expireTime * MILLIS_MINUTE);
		// 根据uuid将customizeUserDetails缓存
		String userKey = getTokenKey(customizeUserDetails.getToken());
		redisCache.setCacheObject(userKey, customizeUserDetails, expireTime, TimeUnit.MINUTES);
	}

	/**
	 * @title 设置用户代理信息
	 * @param customizeUserDetails 登录信息
	 */
	public void setUserAgent(CustomizeUserDetails customizeUserDetails) {
		
		UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
		String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
		customizeUserDetails.setIpaddr(ip);
		customizeUserDetails.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
		customizeUserDetails.setBrowser(userAgent.getBrowser().getName());
		customizeUserDetails.setOs(userAgent.getOperatingSystem().getName());
	}

	/**
	 * @title 从数据声明生成令牌
	 * @param claims 数据声明
	 * @return 令牌
	 */
	private String createToken(Map<String, Object> claims) {
		
		String token = Jwts.builder()
				.setClaims(claims)
				.signWith(SignatureAlgorithm.HS512, secret).compact();
		return token;
	}

	/**
	 * @title 从令牌中获取数据声明
	 * @param token 令牌
	 * @return 数据声明
	 */
	private Claims parseToken(String token) {
		
		return Jwts.parser()
				.setSigningKey(secret)
				.parseClaimsJws(token)
				.getBody();
	}

	/**
	 * @title 获取请求的token
	 * @param request
	 * @return
	 */
	public String getToken(HttpServletRequest request) {
		
		String token = request.getHeader(header);
		if (StringUtils.isNotEmpty(token) && token.startsWith(FilmSystemConstants.TOKEN_PREFIX)) {
			token = token.replace(FilmSystemConstants.TOKEN_PREFIX, "");
		}
		return token;
	}

	private String getTokenKey(String uuid) {
		return FilmSystemConstants.LOGIN_TOKEN_KEY + uuid;
	}

}

